package org.axed.user.client;

import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.Element;
import org.timepedia.exporter.client.Exportable;

/**
 * Holds all data about an event happened from the area.
 */
public class AxedEvent {
	public static final int BLUR                =  1;
    public static final int CLEARUNDOREDOSTACK  =  2;
    public static final int CLICK               =  3;
    public static final int CUT                 =  4;
	public static final int FOCUS               =  5;
    public static final int FONT_SIZE_CHANGED   =  6;
	public static final int INSERT_CHAR         =  7;
    public static final int JOIN_LINE           =  8;
	public static final int KEY_PRESS           =  9; 
    public static final int MOUSE_DOWN          = 10;
    public static final int MOUSE_MOVE          = 11;
    public static final int MOUSE_UP            = 12;
    public static final int MOVE_CURSOR         = 13;
    public static final int INSERT_TEXT         = 15;
	public static final int LINE_HEIGHT_CHANGED = 16;
	public static final int PASTE               = 17;
    public static final int REMOVE_CHAR         = 18;
    public static final int REMOVE_TEXT         = 19;
    public static final int REFRESH             = 20;
	public static final int REPLACE_EXPR        = 21;
	public static final int REDO			    = 22;
    public static final int SCROLL              = 23;
	public static final int SET_CARET_ENABLED   = 24;
    public static final int SET_FONT            = 25;
    public static final int SPLIT_LINE          = 26;
    public static final int UNDO                = 27;

	public static final int MOVETYPE_FOLLOWUP   = 1;
	public static final int MOVETYPE_ARROW      = 2;
	public static final int MOVETYPE_CLICK      = 3;
	public static final int MOVETYPE_EXTERN     = 4;

	public static final int ATOMIC_NONE     =  0;  // do not care about atoms
	public static final int ATOMIC_UNDOREDO = -1; // this is an undo/redo event.

	public static final int ID_NONE          = 0;
	public static final int ID_ENABLE        = 1;
	public static final int ID_FONT          = 2;
	public static final int ID_MOUSE         = 3;
	public static final int ID_INTERVAL_TEXT = 4;
	public static final int ID_LINE          = 5;
	public static final int ID_FONT_CHANGE   = 6;
	public static final int ID_PNT_CHAR      = 7;
	public static final int ID_PNT_TEXT      = 8;
	public static final int ID_PNT_CURSOR    = 9;
	public static final int ID_POINTER       = 10;
	public static final int ID_KEYPRESS      = 11;
	public static final int ID_REPLACE_EXPR  = 12;
	public static final int ID_TEXT          = 13;
	public static final int MAX_ID           = 14;

	/**
	 * Some events need to be undone/redone in combination. 
	 * (like a paste when something is selected deletes text
	 * and enters text)
	 * every value greater than 0 is an id, 0 or less see below.
	 */
	public int atomicID;

	/**
	 * Set to true if this event is to be canceled.
	 */
	public boolean cancel;

	/**
	 * The type of the event.
	 */
	public int type;

	/**
	 * The inner event
	 */
	public Generic e;
 
 	/**
     * @gwt.export
 	 * @gwt.exportClosure
	 */
	public abstract class Generic {
		// none
		public abstract int getId();

		public final AxedEvent common() {
			return AxedEvent.this;
		}
	}

	/**
	 * BLUR
	 * CLEARUNDOREDOSTACK
	 * FOCUS
	 * REFRESH
	 * REDO
	 * SCROLL
	 * UNDO
	 * CUT
     * @gwt.export
	 */
	public class None extends Generic {
		public static final int ID = ID_NONE;
	
		/**
		 * Issues this events.
		 */
		public None issue(int type) {
			AxedEvent.this.type = type;
			return this;
		}

		public int getId() { return ID; }
	};

	/**
	 * SET_CARET_ENABLED
	 */
	public class Enable extends Generic {
		public static final int ID = ID_ENABLE;

		public boolean enabled;

		public Enable issue(int type, boolean enabled) {
			AxedEvent.this.type = type;
			this.enabled = enabled;
			return this;
		}

		public int getId() { return ID; }
	};

	/**
	 * SET_FONT
	 */
	public class Font extends Generic implements Exportable {
		public static final int ID = ID_FONT;
		
		public int font;
		
		public Font issue(int type, int font) {
			AxedEvent.this.type = type;
			this.font = font;
			return this;
		}
		
		public int getId() { return ID; }
	};

	/**
	 * CLICK
	 * MOUSE_DOWN  
	 * MOUSE_MOVE 
	 * MOUSE_UP  
	 */
	public class Mouse extends Generic {
		public static final int ID = ID_MOUSE;
	
		public boolean shiftKey;
		
		public Element target;
	
		public int x;

		public int y;

		public Mouse issue(int type, int x, int y, Element target) {
			AxedEvent.this.type = type;
			this.x = x;
			this.y = y;
			this.target = target;
			return this;
		}
		
		public int getId() { return ID; }
	};

	/**
	 * REMOVE_TEXT
	 */
	public class IntervalText extends Generic {
		public static final int ID = ID_INTERVAL_TEXT;

		public AxedInterval iv;
		
		public String text;

		public IntervalText(AxedArea axed) {
			this.iv = new AxedInterval(axed);
		}

		public IntervalText issue(int type, AxedInterval iv) {
			AxedEvent.this.type = type;
			this.iv.set(iv);
			return this;
		}
		
		public IntervalText issue(int type, AxedPointer p1, AxedPointer p2) {
			AxedEvent.this.type = type;
			this.iv.set(p1, p2);
			return this;
		}

		public IntervalText issue(int type, int line1, int col1, int line2, int col2) {
			AxedEvent.this.type = type;
			this.iv.set(line1, col1, line2, col2);
			return this;
		}
			
		public int getId() { return ID; }
	};

	/**
	 * LINE_HEIGHT_CHANGED
	 */
	public class Line extends Generic {
		public static final int ID = ID_LINE;

		public int line;

		public Line issue(int type, int line) {
			AxedEvent.this.type = type;
			this.line = line;
			return this;
		}

		public int getId() { return ID; }
	};

	/**
	 * FONT_SIZE_CHANGED
	 */
	public class FontChange extends Generic {
		public static final int ID = ID_FONT_CHANGE;

		public Element testcase;
		
		public FontChange issue(int type, Element testcase) {
			AxedEvent.this.type = type; 
			this.testcase = testcase;
			return this;
		}
		
		public int getId() { return ID; }
	}

	/**
	 * INSERT_CHAR
	 * REMOVE_CHAR
	 */
	public class PntChar extends Generic {
		public static final int ID = ID_PNT_CHAR;

		public char ch;

		public AxedPointer pnt;

		public int tag;

		/**
		 * Constructor.
		 */
		public PntChar(AxedArea axed) {
			pnt = new AxedPointer(axed);
		}

		public PntChar issue(int type, int line, int col, char ch, int tag) {
			AxedEvent.this.type = type;
			pnt.set(line, col);
			this.ch = ch;
			this.tag = tag;
			return this;
		}

		public PntChar issue(int type, AxedPointer p, char ch, int tag) {
			AxedEvent.this.type = type;
			pnt.set(p);
			this.ch = ch;
			this.tag = tag;
			return this;
		}
		
		public int getId() { return ID; }
	}

	/**
	 * INSERT_TEXT
	 */
	public class PntText extends Generic {
		public static final int ID = ID_PNT_TEXT;

		public String text;

		/**
		 * interval begin is the place to insert at.
		 * Document will fill in interval end for end of
		 * insert.
		 */
		public AxedInterval iv;
		
		/**
		 * Constructor.
		 */
		public PntText(AxedArea axed) {
			iv = new AxedInterval(axed);
		}

		public PntText issue(int type, int line, int col, String text) {
			AxedEvent.this.type = type;
			iv.p1.set(line, col);
			iv.p2.invalidize();
			this.text = text;
			return this;
		}

		public PntText issue(int type, AxedPointer p, String text) {
			AxedEvent.this.type = type;
			iv.p1.set(p);
			iv.p2.invalidize();
			this.text = text;
			return this;
		}
		
		public int getId() { return ID; }
	}

	/**
	 * MOVE_CURSOR
	 */
	public class PntCursor extends Generic {
		public static final int ID = ID_PNT_CURSOR;

		public AxedPointer pnt;

		int movetype;
		
		/**
		 * Constructor.
		 */
		public PntCursor(AxedArea axed) {
			pnt = new AxedPointer(axed);
		}
	
		public PntCursor issue(int type, int line, int col, int movetype) {
			AxedEvent.this.type = type;
			pnt.set(line, col);
			this.movetype = movetype;
			return this;
		}

		public PntCursor issue(int type, AxedPointer p, int movetype) {
			AxedEvent.this.type = type;
			pnt.set(p);
			this.movetype = movetype;
			return this;
		}
		
		public int getId() { return ID; }
	}

	/**
	 * JOIN_LINE
	 * SPLIT_LINE
	 */
	public class Pointer extends Generic {
		public static final int ID = ID_POINTER;

		public AxedPointer pnt;
		
		/**
		 * Constructor.
		 */
		public Pointer(AxedArea axed) {
			pnt = new AxedPointer(axed);
		}

		public Pointer issue(int type, int line, int col) {
			AxedEvent.this.type = type;
			pnt.set(line, col);
			return this;
		}

		public Pointer issue(int type, AxedPointer pnt) {
			AxedEvent.this.type = type;
			this.pnt.set(pnt);
			return this;
		}
		
		public int getId() { return ID; }
	}

	/**
	 * KEY_PRESS
	 */
	public class KeyPress extends Generic {
		public static final int ID = ID_KEYPRESS;

		public char ch;

		public boolean ctrlKey;

		public boolean shiftKey;

		public int keycode;
		
		public KeyPress issue(int type, int keycode, char ch, boolean shift, boolean control) {
			AxedEvent.this.type = type;
			this.ch = ch;
			this.keycode = keycode;
			this.shiftKey = shift;
			this.ctrlKey = control;
			return this;
		}
		
		public int getId() { return ID; }

		/**
		 * TODO: for export
		 */
		public int getCh() { return ch; }
	}

	/**
	 * REPLACE_EXPR
	 */
	public class ReplaceExpr extends Generic {
		public static final int ID = ID_REPLACE_EXPR;
	
		public AxedInterval iv;

		public String expr;

		public String replacement;

		public boolean global;

		public int replaceNum;
		
		/**
		 * Constructor.
		 */
		public ReplaceExpr(AxedArea axed) {
			iv = new AxedInterval(axed);
		}

		public ReplaceExpr issue(int type, String expr, String replacement,
		                       AxedInterval iv, boolean global) {
			AxedEvent.this.type = type;
			this.expr = expr;
			this.replacement = replacement;
			this.iv.set(iv);
			this.global = global;
			this.replaceNum = 0;
			return this;
		}
		
		public int getId() { return ID; }
	}
	 
	/**
	 * PASTE
	 */
	public class Text extends Generic {
		public static final int ID = ID_TEXT;

		public String text;

		public Text issue(int type, String text) {
			AxedEvent.this.type = type;
			this.text = text;
			return this;
		}
		
		public int getId() { return ID; }
	}


	/**
	 * Returns the class id of a given event type
	 */
	public static int getClassId(int type) {
		switch (type) {
	 	case BLUR :
		case CLEARUNDOREDOSTACK :
		case CUT :
		case FOCUS :
		case REFRESH :
		case REDO :
		case SCROLL :
		case UNDO :
			return None.ID;

	 	case SET_CARET_ENABLED :
			return Enable.ID;

	 	case SET_FONT :
			return Font.ID;

	 	case CLICK :
		case MOUSE_DOWN :
		case MOUSE_MOVE :
		case MOUSE_UP :
			return Mouse.ID;

	 	case REMOVE_TEXT :
			return IntervalText.ID;

	 	case LINE_HEIGHT_CHANGED:
			return Line.ID;

	 	case FONT_SIZE_CHANGED :
			return FontChange.ID;

	 	case INSERT_CHAR :
		case REMOVE_CHAR :
			return PntChar.ID;

	 	case INSERT_TEXT :
			return PntText.ID;

	 	case MOVE_CURSOR :
			return PntCursor.ID;

		case JOIN_LINE :
		case SPLIT_LINE : 
			return Pointer.ID;

		case KEY_PRESS :
			return KeyPress.ID;

		case REPLACE_EXPR :
			return ReplaceExpr.ID;
		
		case PASTE :
			return Text.ID;
		default :
			return -1;
		}
	}

	public final None getBlur() {
		assert e.getId() == ID_NONE;
		return (None) e;
	}

	public final None getClearUndoRedoStack() {
		assert e.getId() == ID_NONE;
		return (None) e;
	}

	public final Mouse getClick() {
		assert e.getId() == ID_MOUSE;
		return (Mouse) e;
	}

	public final None getCut() {
		assert e.getId() == ID_NONE;
		return (None) e;
	}
	
	public final None getFocus() {
		assert e.getId() == ID_NONE;
		return (None) e;
	}

	public final FontChange getFontChange() {
		assert e.getId() == ID_FONT_CHANGE;
		return (FontChange) e;
	}

	public final PntChar getInsertChar() {
		assert e.getId() == ID_PNT_CHAR;
		return (PntChar) e;
	}

	public final Pointer getJoinLine() {
		assert e.getId() == ID_POINTER;
		return (Pointer) e;
	}

	public final KeyPress getKeyPress() {
		assert e.getId() == ID_KEYPRESS;
		return (KeyPress) e;
	}
	
	public final Mouse getMouseDown() {
		assert e.getId() == ID_MOUSE;
		return (Mouse) e;
	}

	public final Mouse getMouseMove() {
		assert e.getId() == ID_MOUSE;
		return (Mouse) e;
	}

	public final Mouse getMouseUp() {
		assert e.getId() == ID_MOUSE;
		return (Mouse) e;
	}

	public final PntCursor getMoveCursor() {
		assert e.getId() == ID_PNT_CURSOR;
		return (PntCursor) e;
	}
	
	public final PntText getInsertText() {
		assert e.getId() == ID_PNT_TEXT;
		return (PntText) e;
	}

	public final Line getLineHeightChanged() {
		assert e.getId() == ID_LINE;
		return (Line) e;
	}

	public final Text getPaste() {
		assert e.getId() == ID_TEXT;
		return (Text) e;
	}

	public final PntChar getRemoveChar() {
		assert e.getId() == ID_PNT_CHAR;
		return (PntChar) e;
	}

	public final IntervalText getRemoveText() {
		assert e.getId() == ID_INTERVAL_TEXT;
		return (IntervalText) e;
	}

	public final None getRefresh() {
		assert e.getId() == ID_NONE;
		return (None) e;
	}

	public final ReplaceExpr getReplaceExpr() {
		assert e.getId() == ID_REPLACE_EXPR;
		return (ReplaceExpr) e;
	}

	public final None getRedo() {
		assert e.getId() == ID_NONE;
		return (None) e;
	}

	public final None getScroll() {
		assert e.getId() == ID_NONE;
		return (None) e;
	}

	public final Enable getSetCaretEnable() {
		assert e.getId() == ID_ENABLE;
		return (Enable) e;
	}

	public final Font getSetFont() {
		assert e.getId() == ID_FONT;
		return (Font) e;
	}

	public final Pointer getSplitLine() {
		assert e.getId() == ID_POINTER;
		return (Pointer) e;
	}

	public final None getUndo() {
		assert e.getId() == ID_NONE;
		return (None) e;
	}

	/**
	 * Creates an event of 'id'-class.
	 */
	public Generic create(int id, AxedArea axed) {
		switch (id) {
		case ID_NONE          : return e = new None();
		case ID_ENABLE        : return e = new Enable();
		case ID_FONT          : return e = new Font();
		case ID_MOUSE         : return e = new Mouse();
		case ID_INTERVAL_TEXT : return e = new IntervalText(axed);
		case ID_LINE          : return e = new Line();
		case ID_FONT_CHANGE   : return e = new FontChange();
		case ID_PNT_CHAR      : return e = new PntChar(axed);
		case ID_PNT_TEXT      : return e = new PntText(axed);
		case ID_PNT_CURSOR    : return e = new PntCursor(axed);
		case ID_POINTER       : return e = new Pointer(axed);
		case ID_KEYPRESS      : return e = new KeyPress();
		case ID_REPLACE_EXPR  : return e = new ReplaceExpr(axed);
		case ID_TEXT          : return e = new Text();
		default : return null;
		}
	}
	
	/**
	 * getType for export
	 */
	public int getType() {
		return type;
	}
}
